package middleware

import (
	"fmt"
	"github.com/gin-gonic/gin"
	retalog "github.com/lestrrat-go/file-rotatelogs"
	"github.com/rifflock/lfshook"
	"github.com/sirupsen/logrus"
	"math"
	"os"
	"time"
)

func Logger() gin.HandlerFunc {
	// 定义日志文件存放路径
	filePath := "log/ginblog"

	// 软链接地址：在根目录下访问最新的日志
	//linkName := "latest_log.log"

	// 定义文件写入路径以及方式
	// 可读可写，如果文件不存在，则创建文件，权限为755
	scr, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0755)
	if err != nil {
		fmt.Println("err:", err)
	}

	// 实例化logrus
	logger := logrus.New()

	// 在实例化后，设置日志输出方式：输出到日志文件中
	logger.Out = scr

	// 设置日志的级别
	logger.SetLevel(logrus.DebugLevel)

	// 实现日志的分割
	logWriter, _ := retalog.New(
		// 设置分割文件名，让其按照年月日进行保存
		filePath+"%Y%m%d.log",
		// 设置最大保存时间，一周
		retalog.WithMaxAge(7*24*time.Hour),
		// 执行分割的时间，24小时进行一次分割
		retalog.WithRotationTime(24*time.Hour),

		/*	// 定义软连接，window下需要管理员权限才会生效
			retalog.WithLinkName(linkName),*/
	)

	// 定义书写的内容
	writeMap := lfshook.WriterMap{
		logrus.InfoLevel:  logWriter,
		logrus.FatalLevel: logWriter,
		logrus.DebugLevel: logWriter,
		logrus.WarnLevel:  logWriter,
		logrus.ErrorLevel: logWriter,
		logrus.PanicLevel: logWriter,
	}
	// 格式化时间
	Hook := lfshook.NewHook(writeMap, &logrus.TextFormatter{
		TimestampFormat: "2006-01-02 15:04:05",
	})

	// 将Hook传入到日志中
	logger.AddHook(Hook)

	// 通过函数进行日志的监听，处理
	return func(c *gin.Context) {
		// 记录开始时间
		startTime := time.Now()
		// 先执行后面的中间件
		c.Next()
		// 计算请求所花费的时间
		stopTime := time.Since(startTime).Milliseconds()
		spendTime := fmt.Sprintf("%d ms", int(math.Ceil(float64(stopTime/1000000.0))))
		// 获取请求名称
		hostName, err := os.Hostname()
		if err != nil {
			hostName = "unknown"
		}
		// 请求状态码
		statusCode := c.Writer.Status()
		// 客户端请求过来的ip
		clientIp := c.ClientIP()
		// 获取客户端信息，也就是浏览器相关信息，类型，版本等
		userAgent := c.Request.UserAgent()
		// 请求的文件长度
		dataSize := c.Writer.Size()
		if dataSize < 0 {
			dataSize = 0
		}
		// 请求方法
		method := c.Request.Method
		// 请求路径
		path := c.Request.RequestURI
		// 使用中间件进行日志相关处理
		entry := logger.WithFields(logrus.Fields{
			"HostName":  hostName,
			"status":    statusCode,
			"SpendTime": spendTime,
			"Ip":        clientIp,
			"Method":    method,
			"Path":      path,
			"DataSize":  dataSize,
			"Agent":     userAgent,
		})

		// 处理系统内部的错误
		if len(c.Errors) > 0 {
			entry.Error(c.Errors.ByType(gin.ErrorTypePrivate).String())
		}

		// 处理状态码错误
		if statusCode >= 500 {
			// 服务端错误
			entry.Error()
		} else if statusCode >= 400 {
			// 处理警告
			entry.Warn()
		} else {
			// 成功处理
			entry.Info()
		}
	}
}
